﻿using System;
using System.Data;
using System.Collections;
using System.Text;
using System.Configuration;

namespace BizElements.Core
{
    #region Documentation.
    /// <summary>
    /// Provides static methods that create predicates.
    /// </summary>
    /// <remarks>Predicate objects cannot be created by using their constructors. The factory class must be used 
    /// to create instances of supported predicates.<seealso cref="PredicateBase"/>
    /// <seealso cref="SearchCondition"/></remarks>
    #endregion
    public static class PredicateFactory
    {
        #region Utility methods.

        private static DbType DetermineDbType(ISqlItem item)
        {
            DbType dbType = DbType.Object;
            if (item.ItemType == SqlItemType.Column)
            {
                dbType = ((IDbColumn)item.Item).DbType;
            }
            else if (item.ItemType == SqlItemType.Parameter)
            {
                dbType = ((DbParameter)item.Item).DbType;
            }
            else if (item.ItemType == SqlItemType.SubQuery)
            {
                SelectStatement query = (SelectStatement)item.Item;
                if (query.SelectList.Count > 0)
                    dbType = DetermineDbType(query.SelectList[0]);
            }

            return dbType;
        }

        private static int DetermineSize(ISqlItem item)
        {
            int size = 0;
            if (item.ItemType == SqlItemType.Column)
            {
                size = ((IDbColumn)item.Item).MaxLengthIfText;
            }
            else if (item.ItemType == SqlItemType.Parameter)
            {
                size = ((DbParameter)item.Item).Size;
            }
            else if (item.ItemType == SqlItemType.SubQuery)
            {
                SelectStatement query = (SelectStatement)item.Item;
                if (query.SelectList.Count > 0)
                    size = DetermineSize(query.SelectList[0]);
            }

            if (size == 0)
            {
                // Non-variable length types.
                return 0;
            }
            else if (size > 0 && size < 100)
            {
                // Round up to 100.
                return 100;
            }
            else
            {
                // Computed value.
                return size;
            }
        }

        private static int DetermineValueSize(object value)
        {
            if (value is ValueType)
                return 0;

            string str = value as string;
            if (str != null)
                return str.Length;

            Array arr = value as Array;
            if (arr != null)
                return arr.Length;
            
            return 0;
        }

        #endregion

        #region Compare a field to another item (parameter, other field, subquery, function).

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/><seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="value">Value. <b>Null</b> is not allowed.</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares a database field to a .NET value (String, Char[], Boolean, 
        /// Char, Byte, Int16, Int32, Int64, Single, Double, Decimal, DateTime etc).</remarks>
        public static ComparePredicate Compare(IDbColumn field, string comparisonOperator, object value)
        {
            if (field == null || comparisonOperator == null || value == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(field, SqlItemType.Column), comparisonOperator,
                new PredicateItem(value, field.DbType, field.MaxLengthIfText, field.GetPrefixedAlias()), false);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="value">Value. <b>Null</b> is not allowed.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT).</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares a database field to a .NET value (String, Char[], Boolean, 
        /// Char, Byte, Int16, Int32, Int64, Single, Double, Decimal, DateTime etc).</remarks>
        public static ComparePredicate Compare(IDbColumn field, string comparisonOperator, object value, bool negate)
        {
            if (field == null || comparisonOperator == null || value == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(field, SqlItemType.Column), comparisonOperator,
                new PredicateItem(value, field.DbType, field.MaxLengthIfText, field.GetPrefixedAlias()), negate);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="leftColumn">Left hand side database field.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="rightColumn">Right hand side database field.</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares two database fields.</remarks>
        public static ComparePredicate Compare(IDbColumn leftColumn, string comparisonOperator, IDbColumn rightColumn)
        {
            if (leftColumn == null || comparisonOperator == null || rightColumn == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(leftColumn, SqlItemType.Column), comparisonOperator,
                new PredicateItem(rightColumn, SqlItemType.Column), false);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="leftColumn">Left hand side database field.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="rightColumn">Right hand side database field.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT).</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares two database fields.</remarks>
        public static ComparePredicate Compare(IDbColumn leftColumn, string comparisonOperator, IDbColumn rightColumn, bool negate)
        {
            if (leftColumn == null || comparisonOperator == null || rightColumn == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(leftColumn, SqlItemType.Column),
                comparisonOperator, new PredicateItem(rightColumn, SqlItemType.Column), negate);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares a field to a value retrieved by subquery.</remarks>
        public static ComparePredicate Compare(IDbColumn field, string comparisonOperator, SelectStatement subQuery)
        {
            if (field == null || comparisonOperator == null || subQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(field, SqlItemType.Column),
                comparisonOperator, new PredicateItem(subQuery, SqlItemType.SubQuery), false);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT).</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares a field to a value retrieved by subquery.</remarks>
        public static ComparePredicate Compare(IDbColumn field, string comparisonOperator, SelectStatement subQuery, bool negate)
        {
            if (field == null || comparisonOperator == null || subQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(field, SqlItemType.Column),
                comparisonOperator, new PredicateItem(subQuery, SqlItemType.SubQuery), negate);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="function">Function.</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares a field to a value computed by function 
        /// (see <see cref="AggregateFunction"/>, <see cref="Function"/>).</remarks>
        public static ComparePredicate Compare(IDbColumn field, string comparisonOperator, Function function)
        {
            if (field == null || comparisonOperator == null || function == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(field), comparisonOperator, new PredicateItem(function), false);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="function">Function.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT).</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares a field to a value computed by function 
        /// (see <see cref="AggregateFunction"/>, <see cref="Function"/>).</remarks>
        public static ComparePredicate Compare(IDbColumn field, string comparisonOperator, Function function, bool negate)
        {
            if (field == null || comparisonOperator == null || function == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(field), comparisonOperator, new PredicateItem(function), negate);
        }

        #endregion

        #region Compare a subquery to another item (parameter, other query, function).

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="value">Value. <b>Null</b> is not allowed.</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares the value retrieved by subquery to a .NET value.
        /// String, Char[], Boolean, Char, Byte, Int16, Int32, Int64, Single, Double, Decimal, DateTime etc.</remarks>
        public static ComparePredicate Compare(SelectStatement subQuery, string comparisonOperator, object value)
        {
            if (subQuery == null || comparisonOperator == null || value == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            DbType dbType = DetermineDbType(subQuery.SelectList[0]);
            int size = DetermineSize(subQuery.SelectList[0]);
            return new ComparePredicate(new PredicateItem(subQuery), comparisonOperator,
                new PredicateItem(value, dbType, size, "par"), false);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="value">Value. <b>Null</b> is not allowed.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT).</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares the value retrieved by subquery to a .NET value.
        /// String, Char[], Boolean, Char, Byte, Int16, Int32, Int64, Single, Double, Decimal, DateTime etc.</remarks>
        public static ComparePredicate Compare(SelectStatement subQuery, string comparisonOperator, object value, bool negate)
        {
            if (subQuery == null || comparisonOperator == null || value == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            DbType dbType = DetermineDbType(subQuery.SelectList[0]);
            int size = DetermineSize(subQuery.SelectList[0]);
            return new ComparePredicate(new PredicateItem(subQuery), comparisonOperator,
                new PredicateItem(value, dbType, size, "par"), negate);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="leftQuery">Left hand side <see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="rightQuery">Right hand side <see cref="SelectStatement"/> that returns a single value.</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares the value retrieved by one query to a value retrieved by another query.</remarks>
        public static ComparePredicate Compare(SelectStatement leftQuery, string comparisonOperator, SelectStatement rightQuery)
        {
            if (leftQuery == null || comparisonOperator == null || rightQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(leftQuery), comparisonOperator, new PredicateItem(rightQuery), false);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="leftQuery">Left hand side <see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="rightQuery">Right hand side <see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT).</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares the value retrieved by one query to a value retrieved by another query.</remarks>
        public static ComparePredicate Compare(SelectStatement leftQuery, string comparisonOperator, SelectStatement rightQuery, bool negate)
        {
            if (leftQuery == null || comparisonOperator == null || rightQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(leftQuery), comparisonOperator, new PredicateItem(rightQuery), negate);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="function">Function.</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares the value retrieved by the query to a value computed by the 
        /// function (see <see cref="AggregateFunction"/>, <see cref="Function"/>).</remarks>
        public static ComparePredicate Compare(SelectStatement subQuery, string comparisonOperator, Function function)
        {
            if (subQuery == null || comparisonOperator == null || function == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(subQuery), comparisonOperator, new PredicateItem(function), false);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="function">Function.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT).</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares the value retrieved by the query to a value computed by the 
        /// function (see <see cref="AggregateFunction"/>, <see cref="Function"/>).</remarks>
        public static ComparePredicate Compare(SelectStatement subQuery, string comparisonOperator, Function function, bool negate)
        {
            if (subQuery == null || comparisonOperator == null || function == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ComparePredicate(new PredicateItem(subQuery), comparisonOperator, new PredicateItem(function), negate);
        }

        #endregion

        #region Compare a function to another item (parameter).

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="function">Function.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="value">Value. <b>Null</b> is not allowed.</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares the value computed by a function 
        /// (see <see cref="AggregateFunction"/>, <see cref="Function"/>) to a .NET value.
        /// String, Char[], Boolean, Char, Byte, Int16, Int32, Int64, Single, Double, Decimal, DateTime etc.</remarks>
        public static ComparePredicate Compare(Function function, string comparisonOperator, object value)
        {
            if (function == null || comparisonOperator == null || value == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            int size = DetermineValueSize(value);
            return new ComparePredicate(new PredicateItem(function), comparisonOperator,
                new PredicateItem(value, TypeUtil.DbTypeFrom(value.GetType()), size, "par"), false);
        }

        /// <summary>
        /// Creates a <see cref="ComparePredicate"/>.<seealso cref="ComparisonOperator"/>
        /// </summary>
        /// <param name="function">Function.</param>
        /// <param name="comparisonOperator"><see cref="ComparisonOperator"/>. Eg: ">=".</param>
        /// <param name="value">Value. <b>Null</b> is not allowed.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT).</param>
        /// <returns>ComparePredicate.</returns>
        /// <remarks>Creates a predicate that compares the value computed by a function 
        /// (see <see cref="AggregateFunction"/>, <see cref="Function"/>) to a .NET value.
        /// String, Char[], Boolean, Char, Byte, Int16, Int32, Int64, Single, Double, Decimal, DateTime etc.</remarks>
        public static ComparePredicate Compare(Function function, string comparisonOperator, object value, bool negate)
        {
            if (function == null || comparisonOperator == null || value == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            int size = DetermineValueSize(value);
            return new ComparePredicate(new PredicateItem(function), comparisonOperator,
                new PredicateItem(value, TypeUtil.DbTypeFrom(value.GetType()), size, "par"), negate);
        }

        #endregion

        #region LIKE.

        /// <summary>
        /// Creates a <see cref="LikePredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="pattern">Pattern. <b>Null</b> is not allowed.</param>
        /// <returns>LikePredicate.</returns>
        public static LikePredicate Like(IDbColumn field, string pattern)
        {
            if (field == null || pattern == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            // NVarChar is always used for parameters because SQL Server has issues
            // when NText parameter type is used with LIKE operator.
            return new LikePredicate(new PredicateItem(field, SqlItemType.Column),
                new PredicateItem(pattern, DbType.String, field.MaxLengthIfText, field.GetPrefixedAlias()), false);
        }

        /// <summary>
        /// Creates a <see cref="LikePredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="pattern">Pattern. <b>Null</b> is not allowed.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT LIKE).</param>
        /// <returns>LikePredicate.</returns>
        public static LikePredicate Like(IDbColumn field, string pattern, bool negate)
        {
            if (field == null || pattern == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new LikePredicate(new PredicateItem(field, SqlItemType.Column),
                new PredicateItem(pattern, DbType.String, field.MaxLengthIfText, field.GetPrefixedAlias()), negate);
        }

        #endregion

        #region IS NULL.

        /// <summary>
        /// Creates an <see cref="IsNullPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <returns>IsNullPredicate.</returns>
        /// <remarks>Creates a predicate that checks whether a database field contains a <b>null</b> value.</remarks>
        public static IsNullPredicate IsNull(IDbColumn field)
        {
            if (field == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new IsNullPredicate(new PredicateItem(field, SqlItemType.Column), false);
        }

        /// <summary>
        /// Creates an <see cref="IsNullPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="negate">Specifies whether the predicate is negated (IS NOT NUL).</param>
        /// <returns>IsNullPredicate.</returns>
        /// <remarks>Creates a predicate that checks whether a database field contains a <b>null</b> value.</remarks>
        public static IsNullPredicate IsNull(IDbColumn field, bool negate)
        {
            if (field == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new IsNullPredicate(new PredicateItem(field, SqlItemType.Column), negate);
        }

        /// <summary>
        /// Creates an <see cref="IsNullPredicate"/>.
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <returns>IsNullPredicate.</returns>
        /// <remarks>Creates a predicate that checks whether the value retrieved the by the query is <b>null</b>.</remarks>
        public static IsNullPredicate IsNull(SelectStatement subQuery)
        {
            if (subQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new IsNullPredicate(new PredicateItem(subQuery), false);
        }

        /// <summary>
        /// Creates an <see cref="IsNullPredicate"/>.
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <param name="negate">Specifies whether the predicate is negated (IS NOT NUL).</param>
        /// <returns>IsNullPredicate.</returns>
        /// <remarks>Creates a predicate that checks whether the value retrieved the by the query is <b>null</b>.</remarks>
        public static IsNullPredicate IsNull(SelectStatement subQuery, bool negate)
        {
            if (subQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new IsNullPredicate(new PredicateItem(subQuery), negate);
        }

        /// <summary>
        /// Creates an <see cref="IsNullPredicate"/>.
        /// </summary>
        /// <param name="function">Function.</param>
        /// <returns>IsNullPredicate.</returns>
        /// <remarks>Creates a predicate that checks whether the value returned the by the function is <b>null</b>.</remarks>
        public static IsNullPredicate IsNull(Function function)
        {
            if (function == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new IsNullPredicate(new PredicateItem(function), false);
        }

        /// <summary>
        /// Creates an <see cref="IsNullPredicate"/>.
        /// </summary>
        /// <param name="function">Function.</param>
        /// <param name="negate">Specifies whether the predicate is negated (IS NOT NUL).</param>
        /// <returns>IsNullPredicate.</returns>
        /// <remarks>Creates a predicate that checks whether the value returned the by the function is <b>null</b>.</remarks>
        public static IsNullPredicate IsNull(Function function, bool negate)
        {
            if (function == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new IsNullPredicate(new PredicateItem(function), negate);
        }

        /// <summary>
        /// Creates a negated <see cref="IsNullPredicate"/>, ie. "IS NOT NULL" expression.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <returns>IsNullPredicate.</returns>
        /// <remarks>Creates a predicate that checks whether a database field does not contain a <b>null</b> value.
        /// Same as <see cref="IsNull(IDbColumn, bool)"/> method when <b>negate</b> parameter is set to <b>true</b>.</remarks>
        public static IsNullPredicate IsNotNull(IDbColumn field)
        {
            if (field == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new IsNullPredicate(new PredicateItem(field, SqlItemType.Column), /*not null*/true);
        }

        /// <summary>
        /// Creates a negated <see cref="IsNullPredicate"/>, ie. "IS NOT NULL" expression.
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/> that returns a single value.</param>
        /// <returns>IsNullPredicate.</returns>
        /// <remarks>Creates a predicate that checks whether a database field does not contain a <b>null</b> value.
        /// Same as <see cref="IsNull(SelectStatement, bool)"/> method when <b>negate</b> parameter is set to <b>true</b>.</remarks>
        public static IsNullPredicate IsNotNull(SelectStatement subQuery)
        {
            if (subQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new IsNullPredicate(new PredicateItem(subQuery), /*not null*/true);
        }

        /// <summary>
        /// Creates a negated <see cref="IsNullPredicate"/>, ie. "IS NOT NULL" expression.
        /// </summary>
        /// <param name="function">Function.</param>
        /// <returns>IsNullPredicate.</returns>
        /// <remarks>Creates a predicate that checks whether a database field does not contain a <b>null</b> value.
        /// Same as <see cref="IsNull(Function, bool)"/> method when <b>negate</b> parameter is set to <b>true</b>.</remarks>
        public static IsNullPredicate IsNotNull(Function function)
        {
            if (function == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new IsNullPredicate(new PredicateItem(function), /*not null*/true);
        }

        #endregion

        #region BETWEEN.

        /// <summary>
        /// Creates a <see cref="BetweenPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="beginValue">Begin value. <b>Null</b> is not allowed.</param>
        /// <param name="endValue">End value. <b>Null</b> is not allowed.</param>
        /// <returns>BetweenPredicate.</returns>
        public static BetweenPredicate Between(IDbColumn field, object beginValue, object endValue)
        {
            if (field == null || beginValue == null || endValue == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new BetweenPredicate(new PredicateItem(field, SqlItemType.Column),
                new PredicateItem(beginValue, field.DbType, field.MaxLengthIfText, field.GetPrefixedAlias() + "_begin"),
                new PredicateItem(endValue, field.DbType, field.MaxLengthIfText, field.GetPrefixedAlias() + "_end"), false);
        }

        /// <summary>
        /// Creates a <see cref="BetweenPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="beginValue">Begin value. <b>Null</b> is not allowed.</param>
        /// <param name="endValue">End value. <b>Null</b> is not allowed.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT BETWEEN).</param>
        /// <returns>BetweenPredicate.</returns>
        public static BetweenPredicate Between(IDbColumn field, object beginValue, object endValue, bool negate)
        {
            if (field == null || beginValue == null || endValue == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new BetweenPredicate(new PredicateItem(field, SqlItemType.Column),
                new PredicateItem(beginValue, field.DbType, field.MaxLengthIfText, field.GetPrefixedAlias() + "_begin"),
                new PredicateItem(endValue, field.DbType, field.MaxLengthIfText, field.GetPrefixedAlias() + "_end"), negate);
        }

        #endregion

        #region IN.

        /// <summary>
        /// Creates an <see cref="InPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="values">List of values.</param>
        /// <returns>InPredicate.</returns>
        /// <remarks>Creates an IN predicate that determines if a field matches any value in a list.</remarks>
        public static InPredicate In(IDbColumn field, object[] values)
        {
            if (field == null || values == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            PredicateItem[] itemValues = new PredicateItem[values.Length];
            for (int valIdx = 0; valIdx < values.Length; valIdx++)
                itemValues[valIdx] = new PredicateItem(values[valIdx], field.DbType, field.MaxLengthIfText, field.GetPrefixedAlias());

            return new InPredicate(new PredicateItem(field, SqlItemType.Column), itemValues, false);
        }

        /// <summary>
        /// Creates an <see cref="InPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="values">List of values.</param>
        /// <returns>InPredicate.</returns>
        /// <remarks>Creates an IN predicate that determines if a field matches any value in a list.</remarks>
        public static InPredicate In(IDbColumn field, Array values)
        {
            object[] objValues = new object[values.Length];
            values.CopyTo(objValues, 0);
            return In(field, objValues);
        }

        /// <summary>
        /// Creates an <see cref="InPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="values">List of values.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT IN).</param>
        /// <returns>InPredicate.</returns>
        /// <remarks>Creates an IN predicate that determines if a field matches any value in a list.</remarks>
        public static InPredicate In(IDbColumn field, object[] values, bool negate)
        {
            if (field == null || values == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            PredicateItem[] itemValues = new PredicateItem[values.Length];
            for (int valIdx = 0; valIdx < values.Length; valIdx++)
                itemValues[valIdx] = new PredicateItem(values[valIdx], field.DbType, field.MaxLengthIfText, field.GetPrefixedAlias());

            return new InPredicate(new PredicateItem(field, SqlItemType.Column), itemValues, negate);
        }

        /// <summary>
        /// Creates an <see cref="InPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="values">List of values.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT IN).</param>
        /// <returns>InPredicate.</returns>
        /// <remarks>Creates an IN predicate that determines if a field matches any value in a list.</remarks>
        public static InPredicate In(IDbColumn field, Array values, bool negate)
        {
            object[] objValues = new object[values.Length];
            values.CopyTo(objValues, 0);
            return In(field, objValues, negate);
        }

        /// <summary>
        /// Creates an <see cref="InPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="subQuery"><see cref="SelectStatement"/> that selects values from a single column.</param>
        /// <returns>InPredicate.</returns>
        /// <remarks>Creates an IN predicate that determines if a field matches any value in a subquery.</remarks>
        public static InPredicate In(IDbColumn field, SelectStatement subQuery)
        {
            if (field == null || subQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            PredicateItem[] itemValues = new PredicateItem[] { new PredicateItem(subQuery, SqlItemType.SubQuery) };
            return new InPredicate(new PredicateItem(field, SqlItemType.Column), itemValues, false);
        }

        /// <summary>
        /// Creates an <see cref="InPredicate"/>.
        /// </summary>
        /// <param name="field">Database field.</param>
        /// <param name="subQuery"><see cref="SelectStatement"/> that selects values from a single column.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT IN).</param>
        /// <returns>InPredicate.</returns>
        /// <remarks>Creates an IN predicate that determines if a field matches any value in a subquery.</remarks>
        public static InPredicate In(IDbColumn field, SelectStatement subQuery, bool negate)
        {
            if (field == null || subQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            PredicateItem[] itemValues = new PredicateItem[] { new PredicateItem(subQuery, SqlItemType.SubQuery) };
            return new InPredicate(new PredicateItem(field, SqlItemType.Column), itemValues, negate);
        }

        #endregion

        #region EXISTS.

        /// <summary>
        /// Creates an <see cref="ExistsPredicate"/> that checks if a given query returns any rows.
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/>.</param>
        /// <returns>ExistsPredicate.</returns>
        public static ExistsPredicate Exists(SelectStatement subQuery)
        {
            if (subQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ExistsPredicate(new PredicateItem(subQuery, SqlItemType.SubQuery), false);
        }

        /// <summary>
        /// Creates an <see cref="ExistsPredicate"/> that checks if a given query returns any rows.
        /// </summary>
        /// <param name="subQuery"><see cref="SelectStatement"/>.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT EXISTS).</param>
        /// <returns>ExistsPredicate.</returns>
        public static ExistsPredicate Exists(SelectStatement subQuery, bool negate)
        {
            if (subQuery == null)
                throw new ArgumentException(Messages.PredicateFactory_ObjReferenceIsNull);

            return new ExistsPredicate(new PredicateItem(subQuery, SqlItemType.SubQuery), negate);
        }

        #endregion

        #region CONTAINS.

        /// <summary>
        /// Performs full-text search which returns all records which contain the provided word or exact phrase.
        /// </summary>
        /// <param name="field">Textual database field for which full-text index has been created.</param>
        /// <param name="wordOrPhrase">One or more words with spaces between each word.</param>
        /// <returns>CONTAINS expression or a compatibile LIKE predicate if CONTAINS is not enabled or supported by target DBMS.</returns>
        /// <remarks>The provided phrase should only contain letters, digits, undescores and space characters.
        /// All other characters are treated as delimiters and converted to a space character.</remarks>
        public static ContainsPredicate Contains(IDbColumn field, string wordOrPhrase)
        {
            return new ContainsPredicate(ContainsTermType.SimpleTerm, field, wordOrPhrase, false);
        }

        /// <summary>
        /// Performs full-text search which returns all records which contain the provided word or exact phrase.
        /// </summary>
        /// <param name="field">Textual database field for which full-text index has been created.</param>
        /// <param name="wordOrPhrase">One or more words with spaces between each word.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT CONTAINS).</param>
        /// <returns>CONTAINS expression or a compatibile LIKE predicate if CONTAINS is not enabled or supported by target DBMS.</returns>
        /// <remarks>The provided phrase should only contain letters, digits, undescores and space characters.
        /// All other characters are treated as delimiters and converted to a space character.</remarks>
        public static ContainsPredicate Contains(IDbColumn field, string wordOrPhrase, bool negate)
        {
            return new ContainsPredicate(ContainsTermType.SimpleTerm, field, wordOrPhrase, negate);
        }

        /// <summary>
        /// Performs full-text search which returns all records which contain the provided word prefix(es).
        /// </summary>
        /// <param name="field">Textual database field for which full-text index has been created.</param>
        /// <param name="prefix">One or more words with spaces between each word.</param>
        /// <returns>CONTAINS expression or a compatibile LIKE predicate if CONTAINS is not enabled or supported by target DBMS.</returns>
        /// <remarks>The provided value should only contain letters, digits, undescores and space characters.
        /// All other characters are treated as delimiters and converted to a space character.</remarks>
        public static ContainsPredicate ContainsPrefix(IDbColumn field, string prefix)
        {
            return new ContainsPredicate(ContainsTermType.SimpleTerm, field, prefix, false);
        }

        /// <summary>
        /// Performs full-text search which returns all records which contain the provided word prefix(es).
        /// </summary>
        /// <param name="field">Textual database field for which full-text index has been created.</param>
        /// <param name="prefix">One or more words with spaces between each word.</param>
        /// <param name="negate">Specifies whether the predicate is negated (NOT CONTAINS).</param>
        /// <returns>CONTAINS expression or a compatibile LIKE predicate if CONTAINS is not enabled or supported by target DBMS.</returns>
        /// <remarks>The provided value should only contain letters, digits, undescores and space characters.
        /// All other characters are treated as delimiters and converted to a space character.</remarks>
        public static ContainsPredicate ContainsPrefix(IDbColumn field, string prefix, bool negate)
        {
            return new ContainsPredicate(ContainsTermType.SimpleTerm, field, prefix, negate);
        }

        #endregion
    }	
}